package middleware

import (
	"fmt"
	"net/http"

	"gitee.com/hp-huiw/my-go-core/date"
	libsecurity "gitee.com/hp-huiw/my-go-core/security"
	"github.com/gin-gonic/gin"
	"github.com/go-playground/validator/v10"
)

func GlobalErrorHandlerMiddleware() gin.HandlerFunc {
	return func(c *gin.Context) {
		defer func() {
			if r := recover(); r != nil {
				err := toError(r)
				handleError(c, err)
				c.Abort()
			}
		}()

		// Executes handlers
		c.Next()

		// Handles errors set by c.Error()
		if len(c.Errors) > 0 {
			for _, err := range c.Errors {
				handleError(c, err.Err)
			}

			c.Abort()
		}
	}
}

func toError(r any) error {
	if err, ok := r.(error); ok {
		return err
	}

	return fmt.Errorf("%v", r)
}

func handleError(ctx *gin.Context, err error) {
	var message any
	error := "INTERNAL_SERVER_ERROR"
	path := ctx.FullPath()
	status := http.StatusInternalServerError
	timestamp := date.GetCurrentDate().Unix()

	switch {
	case isValidationError(err):
		message = getValidationErrors(err)
	default:
		// Uses the error message directly if not validation error
		message = err.Error()
	}

	ctx.JSON(status, libsecurity.HandlerErrorResponse{
		Error:     error,
		Message:   message,
		Path:      path,
		Status:    status,
		Success:   false,
		Timestamp: timestamp,
	})
}

func isValidationError(err error) bool {
	_, ok := err.(validator.ValidationErrors)
	return ok
}

func getValidationErrors(err error) map[string]string {
	var ve validator.ValidationErrors
	errors := make(map[string]string)

	if errs, ok := err.(validator.ValidationErrors); ok {
		ve = errs

		for _, fe := range ve {
			field := fe.Field()

			switch fe.Tag() {
			case "required":
				errors[field] = field + " can't be blank"
			case "max":
				errors[field] = field + " must be at most " + fe.Param() + " characters"
			case "min":
				errors[field] = field + " must be at least " + fe.Param() + " characters"
			// add more tag mappings here if needed
			default:
				errors[field] = fe.Error() // fallback
			}
		}
	}

	return errors
}
